0. 符号约定
我们用大写字母表示多项式,小写字母表示多项式的系数,比如:
表示单位根,即
1. FFT
0. 基本思路
考虑对两个多项式进行乘法:
显然系数满足
形如
的式子叫作卷积。
对于多项式乘法,所计算的就是加法卷积。
朴素计算显然是 的,很不优秀。这种不优秀实质上来源于系数表示,我们需要用其他的方法表示多项式。
注意到对于 次多项式,我们可以用 个不同的点处的值来进行唯一表示。因此我们可以对多项式 求出其点值 ,对于多项式 同理,求出 后,乘积 对应的点值显然有
然后再进行插值即可得到 的系数表示。
如此我们将 的朴素卷积转化为了 的点值的乘法,关键在于求点值和插值两步的优化。
1. 求点值(DFT)
如果随意代入 ,复杂度仍为 ,没有优化。但是 是可以任意选取的,我们可以选取更容易计算的点进行计算。这里的技巧是代入单位根 。
设 (次数不够时高次项系数视为 即可),有
利用单位根 的性质,我们能够轻易处理数列的拉伸操作,进而通过分治解决问题。
考虑数列 ,拉伸变换将数列变为
在生成函数(以数列的每一项作为系数的幂级数)的语境下,
设 ,拉伸后的序列对应的幂级数即为 。
这里以二分治为例。方便起见将 补为 的幂。
奇偶分组便于使用拉伸操作:
设 ,,则有
代入单位根立得
容易发现 与 即为两个子问题,因此可以分治求解……吗?
这里有一个问题,子问题中 的取值仅为 ,还有另一半需要计算。
因此我们还需要代入 ,进行计算得
至此我们已经完成了快速计算点值的操作。由 Master 定理易知时间复杂度 。
实质上 DFT 是一个序列变换的过程,它将 变换为了 。
并且这是一个线性的过程:
设 DFT 将序列 变换为序列 ,记作 ,根据线性性得到:
2. 插值(IDFT)
利用上面的观点,容易发现 IDFT 只需把上面矩阵的逆矩阵求出即可。而上面的矩阵是 Vandermonde 矩阵,逆矩阵熟知。
如果不会线代也可以直接启动拉格朗日插值:
注意,利用单位根的性质,后面那个乘积项实际上是可以直接算出的,分子:
分母:
于是
代入整理得
于是(这里更换了下标以保证形式与 DFT 的统一)
对比 DFT 的式子
发现 IDFT 只不过是将单位根换成了 并多了一个 的系数。
在性质上我们实际上无法区分 和 ,算法与先前 DFT 完全一致。
上面关于 IDFT 的证明虽然直接但略显复杂,一个简单一些的证明可以看容斥与反演概论的最后一章。
3. 代码实现
To be finished